/*-------------------------------------------------------------------------


***************** Modification History ***********************************
   @doc
   @module ppicge.c - PP501 functionality|
   @end
   
$Log: /SoftPay/SPK2SIM/Source/PINPad/ppicge.c $ 
 * 
 * 11    3/07/00 6:41p Brett_a1
 * added INCREMENT_PINPAD_ERROR at pinpad failure points
 * 
 * 10    2/21/00 3:00p Jim_b4
 * Include file restructuring
 * 
 * 9     2/21/00 11:29a Jim_b4
 * Include directory restructuring
 * 
 * 8     1/31/00 10:40a Page_k1
 * Change kbd_pending_test to act_kbd_pending_test 
 * 
 * 7     1/26/00 6:42p Page_k1
 * Remove first parameter from vdGetMessageFromFile and
 * vdDisplayMessageFromFile
 * 
 * 6     10/22/99 9:13a Page_k1
 * Change lnSPDHSeqNum to lnSTAN
 * 
 * 5     10/22/99 9:12a Page_k1
 * Change lnSPDHSeqNum to lnSTAN
 * 
 * 4     9/23/99 2:51p Monique_b1
 * Chaned  comm/packet.h to  protocol/packet.h
 * 
 * 3     9/13/99 8:30a Chris_s1
 * Changes made to primarily prototypes to conform to the Motorola 68000
 * format...
 * 
 * 2     8/17/99 12:09p Dom_b1
 * 
 * 1     7/19/99 5:42p Jim_b4
 * 
 * 1     7/19/99 4:26p Jim_b4
 * 
 * 8     6/29/99 5:32p Nick_t1
 * 
 * 7     6/25/99 5:30p Nick_t1
 * 
 * 6     6/25/99 2:26p Nick_t1
 * 
 * 5     6/24/99 5:52p Nick_t1
 * 
 * 4     6/24/99 4:14p Nick_t1
 * 
 * 3     6/23/99 11:56a Nick_t1
 * 
 * 2     6/09/99 11:20a Monique_b1
 * Change headers and file names for SP 2000
**************************************************************************

   Copyright (C) 1999 by VeriFone, Inc.

   All rights reserved.  No part of this software may be reproduced,
   transmitted, transcribed, stored in a retrieval system, or translated
   into any language or computer language, in any form or by any means,
   electronic, mechanical, magnetic, optical, chemical, manual or otherwise,
   without the prior written permission of VeriFone, Inc.

---------------------------------------------------------------------------*/
#define VSPPICGE_C

#include <project.h>
#ifdef PP501_ICPA

#include <io.h>
#include <fcntl.h>
#include <ioctl.h>
#include <ascii.h>
#include <stdio.h>
#include <stdlib.h>
#include <device.h>
#include <acldev.h>
#include <string.h>
#include <aclstr.h>
#include <config.h>
#include <portdef.h>
#include <aclconio.h>
#include <aclascii.h>
#include <message.h>
#include <applidl.h>
#include <actutil.h>

#if defined (_VSC)
// #include <txostd.h>
// #include <txosvc.h>
// #include <txoio.h>
#endif

/** /#define DEBUG_VSPPICGE*/
#ifdef DEBUG_VSPPICGE
#define DEBUG_95
#endif

#include <define.h>
#include <transact.h>
#include <packet.h>
#include <PPICGE.h>
#include <sizes.h>
#include <comm.h>
#include <h00.h>
#include <tfm.h>
#include <pinpad.h>
#include <init.h>
#include <msg.h>
#include <msgfuncs.h>
#include <form.h>
#include <pform.h>
#include <print.h>
#include <ui.h>
#include <spdebug.h>
#include <tran.h>

#if defined (_VSC)
#include <format.h>
#include <date.h>
#include <pspup.h>
#include <pspdh.h>
#include <pnhost.h>
#include <tip.h>
#endif

#define ICPA_CMD_LEN             3

#define ICPA_S09_SIZE           10
#define ICPA_S69_SIZE           20
#define ICPA_S70_SIZE          110
#define ICPA_S71_SIZE          180
#define ICPA_S72_SIZE          196
#define ICPA_S84_SIZE           52

#define ICPA_S95_RESP   61
#define ICPA_SERNUM_LOC 49

extern int inPINPad;              /* Handle to the opened Pinpad device */
extern long ulTimerCount;

/*-----------------------------------------------------------------------------
* Routine Name  : inInitPINPad501
* Description   : This routine will:
*               :  -> Open the Pinpad port.
*                  -> initialise the Pinpad port to the required Baud rate,
*               :  -> message data format, protocol, and packet start and end
*                  -> characters if necessary.
*
* Input         :  -> Baud rate.
* Output        :  -> VS_SUCCESS if the Pinpad port is not opened.
*                  -> VS_ERR if any error in process.
* Notes         :
*                  -> Only the Baud rate and format are parameters.
*                                             Other pinpad
*                  -> initialisation factors (Given Below) are assumed
*                  -> constant for all applications and pinpads.
*                  -> Auto enable and RTS assert.
*                  -> Protocol - packet mode.
*                  -> Packet start char is 0x02 and end character is 0x03.
*
* Modification History:
*
      #     Date      Who                     Description
    ----- -------- ---------- ---------------------------------------------
      1   01/14/99 Page_K1    Delay to allow pinpad to reboot
      2   02/01/99 Page_K1    Increase delay to 2000
      3   02/26/99 Page_K1    Remove unused local variable
*----------------------------------------------------------------------------*/
int inInitPINPad501() {
    /* Set Init params */
    struct Opn_Blk srPINPadInit;            /* Structure used for pinpad parm.init */

    /** /pdebug(("-- inInitPINPad501 --"));*/
    srPINPadInit.rate = inGetPinBarBaudRate();      /* Info found in TCT */
    srPINPadInit.format = inGetPinBarDataFormat();  /* Info found in TCT */
    srPINPadInit.protocol = P_pakt_mode;
    srPINPadInit.parameter = 0;
    srPINPadInit.trailer.packet_parms.stx_char = STX; /* defined-ascii.h      */
    srPINPadInit.trailer.packet_parms.etx_char = ETX; /* defined-ascii.h      */
    srPINPadInit.trailer.packet_parms.count = 1;

    /* This delay allows pinpad to reboot */
    vdDelay(2000);                
    /* Call Open PINPAD */
    if (inPINPad != -1)                             /* If already open close the device */
        if (close(inPINPad) != 0)
            return(VS_ERR);

    inPINPad = open(DEV_PINPAD, 0);     /* Open the port */

    /** /pdebug(("inPINPad=%d",inPINPad));*/
    if (inPINPad < 0)
        return(VS_ERR);
    //NKJTIOCTLFIX
    //  if(ioctl(inPINPad, SetCtrl | 0, &srPINPadInit))
    if (set_opn_blk(inPINPad, &srPINPadInit)) {
        /** /pdebug(("inInitPINPad501 Err"));*/
        return(VS_ERR);                 /* Error setting the port's parameters  */
    } else {
        /** /get_port_status(inPINPad, szBuffer);*/
        /** /pdebug(("Signal=%02x",szBuffer[3]));*/
        /** /pdebug(("inInitPINPad501 success"));*/
        return(VS_SUCCESS);             /* Success in setting the port's params */
    }
}

/*-----------------------------------------------------------------------------
* Routine Name     : vdPP501AdvTransFailed
* Description      : This routine will display a message advising the trans failed
*
* Inputs           :
* Outputs          :
* Notes            :
*
*----------------------------------------------------------------------------*/

void vdPP501AdvTransFailed(TRANSACTION_OBJECT* pobTran,char* szRespCode,char* pszISOCode) {
    char szPPmsg[ICPA_S09_SIZE + 1];

    /** /pdebug(("-- vdPP501AdvTransFailed --"));*/

    memset(szPPmsg, 0x00, ICPA_S09_SIZE + 1);       /* Clear out the buffer */

    /* Build the advice request packet ***************************************/

    szPPmsg[0] = 'S';
    szPPmsg[1] = '0';
    szPPmsg[2] = '9';
    szPPmsg[ICPA_CMD_LEN] = pobTran->srBRec.inLCTIndex + 0x30;  /* Get Lang Code */

    strcat(&szPPmsg[ICPA_CMD_LEN + 1], szRespCode);
    szPPmsg[strlen(szPPmsg)] = FS;  /* Field separator */
    strcat(szPPmsg, pszISOCode);
    szPPmsg[ICPA_S09_SIZE] = 0x00;

    /** /pdebug(("PktLen = %i", strlen(szPPmsg)));*/

    inSendPINPadData(szPPmsg, inGetPPTimeOut(), VS_TRUE);
}


/*-------------------------------------------------------------------------
    Function : inGGIdlePromptID
    Description : This function gets the idle prompt ID from the HDT
    Parameters : Pointer to ID buffer
    Returns :
    Globals : none.
    Notes : none.
    Modification History :
      #     Date      Who                     Description
    ----- -------- ---------- ---------------------------------------------
      1   05/30/95 Carlos_R1  Created w/ minimum req's
--------------------------------------------------------------------------*/
/*
static void vdGGIdlePromptID (pchIDBuff)
char * pchIDBuff;
{
    if (!get_env("#PP_IDLE", pchIDBuff, 1) )
    {
         put_env("#PP_IDLE", "0", 1);
         *pchIDBuff = '0';
    }

    *(pchIDBuff+1) = 0x00;  */      /* Null it! */
/*}
*/


/*-----------------------------------------------------------------------------
* Routine Name     : inSetPP501IdlePrompt
* Description      : This routine will determine what is the idle prompt to be
*                    displayed on the pinpad.
* Inputs           : The string to be displayed on the pinpad.
* Output           : VS_SUCCESS if normal operation.
*                    VS_ERR if any error in Process.
* Notes            : While displaying on the PP501, this routine can be modified
*                    to either 2 strings on the 2 lines or break the string into
*                    2 halves.
*
* Modification History:
*
*        #    Date      Who             Why and What
*       ---  -------- ---------  --------------------------
*        1            Carlos_R1  Created
*        2            Carlos_R1  Shortened Delay to 2 secs
         3   02/26/99 Page_K1    Remove unreferenced variable
*----------------------------------------------------------------------------*/

int inSetPP501IdlePrompt() {
    char pchPPBuf[ICPA_CMD_LEN + 2];

    memset(pchPPBuf, 0x00, ICPA_CMD_LEN + 2);

    /** /pdebug(("-- inSetPP501IdlePrompt --"));*/

    pchPPBuf[0] = 'S';
    pchPPBuf[1] = '0';
    pchPPBuf[2] = '1';

    pchPPBuf[ICPA_CMD_LEN + 1] = 0x00;

    if (inSendPINPadData(pchPPBuf, 2, VS_TRUE) != VS_SUCCESS) {
        /** /pdebug(("inSetPP501IdlePrompt Err=%d",inRetVal));*/
        return(VS_ERR);
    } else {
        /** /pdebug(("inSetPP501IdlePrompt Success"));*/
        return(VS_SUCCESS);
    }
}


/*-----------------------------------------------------------------------------
* Routine Name     : inResetPINPad501
* Description      : Will restore the pinpad to the idle state. Can be called
*                    after a normal operation is complete or when the operation
*                    is aborted.
* Inputs           : None.
* Outputs          : VS_SUCCESS if normal operation
*                  : VS_ERR if any error.
* Notes            : None.
*
* Modification History:
*
*        #       Who                  Why and What
*       ---    ---------       --------------------------
*        1     Carlos_R1       Created
*        2     Jose_F1         Modified timer to 2 sec. 10/01/96
*-----------------------------------------------------------------------------*/

int inResetPINPad501() {
    char pchPINPadBuf[ICPA_CMD_LEN + 1];

    /*  pdebug(("-- inResetPINPad501 --")); */

    pchPINPadBuf[0] = 'S';
    pchPINPadBuf[1] = '0';
    pchPINPadBuf[2] = '1';

    if (inSendPINPadData(pchPINPadBuf, 2, VS_TRUE) != VS_SUCCESS)
        return(VS_ERR);
    else
        return(VS_SUCCESS);
}


/*-------------------------------------------------------------------------
    Function : inGetICPATransCode
    Description : This function gets the transaction code
    Parameters : Pointer to buffer
    Returns :
    Globals : none.
    Notes : none.
    Modification History :
      #     Date      Who                     Description
    ----- -------- ---------- ---------------------------------------------
      1   05/31/95 Carlos_R1  Created w/ minimum req's
      2   12/11/98 Page_K1    Add REFUND_DEBIT
      3   03/08/99 Page_K1    Switch on TRT Tran Code
--------------------------------------------------------------------------*/

static int inGetICPATransCode(TRANSACTION_OBJECT* pobTran,unsigned char* pchIDBuff) {
    int inTranType;

    /*  pdebug(("-- inGetICPATransCode --"));  */

    pchIDBuff[0] = 0x00;                            /* Clear out buffer     */
    pchIDBuff[1] = 0x00;                            /* Clear out buffer     */

    switch (shGetTransactionCode(pobTran)) {
    case SALE:
        pchIDBuff[0] = '0';                     /* Insert ICPA Tran Code*/
        break;
    case REFUND:
    case REFUND_DEBIT:
        pchIDBuff[0] = '1';                     /* Insert ICPA Tran Code*/
        break;
    case DEBIT_BAL_INQ:
        pchIDBuff[0] = '2';                     /* Insert ICPA Tran Code*/
        break;
    case VOID:
    case VOID_REFUND:
        inTranType = pobTran->srBRec.inOrgCode;         /* Get original trans   */
        /* pdebug(("Org Trans Type: %d", inTranType)); */

        switch (inTranType) {
        case SALE:
            /* Purchase Correction  */
            pchIDBuff[0] = '3';             /* Insert ICPA Tran Code*/
            break;
        case REFUND:
            /* Refund Correction    */
        case REFUND_DEBIT:
            /* Refund Correction    */
            pchIDBuff[0] = '4';             /* Insert ICPA Tran Code*/
            break;
        default:
            /* pdebug(("Unknown Org Trans Type: %d", inTranType)); */
            return(VS_ERR); /* An error occurred...trans not allowed */
        }
        break;
    default:
        /* pdebug(("Unknown Trans Type: %d", inTranType)); */
        return(VS_ERR);         /* An error occurred...trans not allowed */
    }

    /* pdebug(("ICPA Trans Type: %c", pchIDBuff[0])); */

    return(VS_SUCCESS);
}


/*-------------------------------------------------------------------------
    Function : inGetPinAccMac_PP501
    Description : This functions processes an Interac request trans on the PP
    Parameters :
    Returns : TRUE or FALSE (error/no error)
    Globals : none.
    Notes : none.
    Modification History :
      #     Date      Who                     Description
    ----- -------- ---------- ---------------------------------------------
      1   05/30/95 Carlos_R1  Created w/ minimum req's
      2   06/01/95 Carlos_R1  Created packet format and send/recv data
      3   06/26/95 Carlos_R1  Completed the function
      4   07/07/95 Carlos_R1  Added Balance Inquiry Support
      5   10/26/95 Jose_F1    Added call to pPrnErrMsg to print receipt when
                              customer selects PinPad Cancel key.
      6   11/27/95 Jose_F1    Added the real Swipe Track2 including the start
                              and end sentinels.
      7   11/28/95 Jose_F1    Do not Use SPDH Trans. No from HDT Stand. No.
      8   01/06/96 Jose_F1    Added error chequing on return inRecPINPadData
                              for message not received.
      9   08/20/96 Jose_F1    Added checking for reversal pending.
     10   09/09/96 Jose_F1    Added Prompt index to GDS.
     11   10/04/96 Jose_F1    Added PINPad on/off checking.
     12   12/02/96 Jose_F1    Added Receipt for debit checking on/off.
     13   11/20/98 Page_K1    Use vdSetTranError() on PP error.
     14   12/09/98 Page_K1    Remove check on inReversalPending()
     15   12/10/98 Page_K1    Do not increment Seq Number here.
     16   12/11/98 Page_K1    Allow for correction request from PINPad
     17   12/14/98 Page_K1    Change check on card type to use
                              pobTran->srBRec.inTranCardType instead of
                              inGetCDTType().
     18   12/21/98 Page_K1    Get receipt template if transaction cancelled.
     19   12/21/98 Page_K1    Print Error receipt if PINPad cancelled
     20   01/08/99 Page_K1    If PP_CANCEL check TRT type and update.
     21   01/12/99 Page_K1    Return VS_ERR instead of VS_ESCAPE on PINPad error
     22   02/19/99 Page_K1    Pad the MAC data term id to 16 with trailing spaces.
     23   02/26/99 Page_K1    Remove unused local variables
     24   02/26/99 Page_K1    Change PINPad Enter PIN message from WARNING_MSG
                                to STATUS_MSG to save time
     25   03/05/99 Page_K1    Do not send amt in MAC data if BAL_INQ. If BAL_INQ
                                send current seq number for MACing.
     26   03/08/99 Page_K1    Set VOID_REFUND if appropriate.
                              Do not set lnSPDHSeqNum = 0L if BAL_INQ.
 ------------------------------------------------------------------------*/

int inGetPinAccMac_PP501(TRANSACTION_OBJECT* pobTran) {
    H00_REC srH00;
    TFM_REC srTFMRec;
    unsigned char szScratch[20];

    unsigned char szRequestPkt[ICPA_S70_SIZE + 1];
    unsigned char szTempBuff[45];

    int inResult;
    int inRetVal = 0;
    int inTranCode;
    /** / int i;*/

    /** /pdebug(("--inGetPinAccMac_PP501--"));*/

    if (pobTran->srBRec.inTranCardType != DEBIT_CARD)
        return (VS_SUCCESS);

    if (inRetVal == VS_ERR || act_kbd_pending_test(KEY_CANCEL)) {
        inResetPINPad501();       /* Return to idle */
        return(VS_ERROR);
    }

    do {
        /*
         *      For terminals with integrated pinpad/display, do not display this message
         */
        //      if ( !fIntPINPad() )
        //          vdDisplayMessageFromFile( PINENTRY_MSG, STATUS_MSG);

        /** /pdebug(("-- inGetPinAccMac --"));*/
        memset(szRequestPkt, 0x00, ICPA_S70_SIZE + 1);  /* Clear out the buffer */
        memset(szTempBuff, 0x00, 45);           /* Clear out the buffer         */
        memset(szScratch, 0x00, 20);

        /* Build the request packet *********************************************/

        szRequestPkt[0] = 'S';
        szRequestPkt[1] = '7';
        szRequestPkt[2] = '0';

        sprintf(&szRequestPkt[ICPA_CMD_LEN], "%c", (pobTran->srBRec.inLCTIndex + '0'));

        if (inGetICPATransCode(pobTran, szTempBuff))        /* Get Trans Code               */
            return(VS_ERR);                         /* Return Error                 */

        strcat(szRequestPkt, szTempBuff);       /* Append to request packet     */

        sprintf(szTempBuff, SUBSTITUTE_LD, pobTran->srBKey.lnTotalOfTransactionAmount);
        vdFormatAmount(szTempBuff, VS_FALSE);               /* Format the amount for PP */
        vdTrimLeadSpaces(szTempBuff);
        strcat(szRequestPkt, szTempBuff);       /* Append to request packet     */

        /* Below is the data that will be MACed *********************************/

        szTempBuff[0] = FS;
        strcpy(&szTempBuff[1], "00");              /* Do not use SPDH Trans. No.   */
        /** /pdebug(("TransNo = %s", szTempBuff));*/
        strcat(szRequestPkt, szTempBuff);       /* Append to request packet     */

        inLoadNCRParams(&srH00);
        strcpy(szTempBuff, srH00.szTermNum);
        pad(szTempBuff, szTempBuff, ' ', TERM_ID_SIZE, LEFT);
        /** /pdebug(("TermId = %s", szTempBuff));     Must be 16 bytes, as per SPDH*//**/
        strcat(szRequestPkt, szTempBuff);       /* Append to request packet     */

        inTranCode = shGetTransactionCode(pobTran);
        if ((inGGCode() == REFUND || inGGCode() == REFUND_DEBIT) && (shGetTransactionCode(pobTran) == VOID))
            inTranCode = VOID_REFUND;

        /** /pdebug(("TranCode = %d", shGetTransactionCode(pobTran)));*/
        inGetTFMTransFields(inTranCode, &srTFMRec, SPDH_SECOND_FID_SET);
        /** /pdebug(("TranCo = %d", srTFMRec.inSPDHTranCode));*/
        int2str(szTempBuff, srTFMRec.inSPDHTranCode);
        pad(szTempBuff, szTempBuff, '0', 2, RIGHT);
        strcat(szRequestPkt, szTempBuff);       /* Append to request packet     */

        if (inGGCode() != DEBIT_BAL_INQ) {
            sprintf(szTempBuff, SUBSTITUTE_LD, pobTran->srBKey.lnTotalOfTransactionAmount);
            /** /pdebug(("Amt = %s", szTempBuff));*/
            strcat(szRequestPkt, szTempBuff);       /* Append to request packet     */
        }

        /*      if ( inGGCode() == DEBIT_BAL_INQ )
                    pobTran->srBRec.lnSPDHSeqNum = 0L;
        */

        vdFormSPDHSeqNum(pobTran);                      /* Create the number            */
        sprintf(szTempBuff, SUBSTITUTE_LD, pobTran->srBRec.lnSTAN);
        /** /pdebug(("SeqNum = %s", szTempBuff));*/
        pad(szTempBuff, szTempBuff, '0', 10, RIGHT);
        strcat(szRequestPkt, szTempBuff);       /* Append to request packet     */

        memset(szTempBuff, 0x00, 45);           /* Clear out the buffer         */
        szTempBuff[0] = ';';
        memcpy(&szTempBuff[1], (char *) pobTran->srBRec.stT2Data, /*SPDH_*/TRACK2_SIZE);
        szTempBuff[strlen(szTempBuff)] = '?';
        /** /pdebug(("T2=%s", szTempBuff));*/
        strcat(szRequestPkt, szTempBuff);       /* Append to request packet     */

        /** /pdebug(("PktLen = %i", strlen(szRequestPkt)));  */
        /** /pdebug(("RP=%s", szRequestPkt));*/
        /*
         *  For terminals with integrated pinpad/display, do not display this message
         */
        /** /   
        i=0;
        while (szRequestPkt[i] != NULL_CH)
        {
            pdebug(("PP70-%d-%02x",i,szRequestPkt[i]));
            i++;
        }
        */

        if ((inRetVal = inSendPINPadData(szRequestPkt, inGetPPTimeOut(), VS_TRUE)) == VS_ERR) {
            /**/pdebug(("Req Xmit Fail")); /**/
            return(VS_ESCAPE);
        }
        /**/pdebug(("PPRetVal=%d", inRetVal));/**/

        memset(szRequestPkt, 0x00, ICPA_S70_SIZE + 1);  /* Clear out the buffer */

        inResult = inRecPINPad501Data(szRequestPkt, inGetPPTimeOut(), sizeof(szRequestPkt));
        /**/pdebug(("PPResult=%d", inResult));/**/
        if (inResult == VS_ERR)
            return(VS_ESCAPE);

        /* Analyze the response *************************************************/
        /** /pdebug(("RESP=%s", szRequestPkt));*/

        if (szRequestPkt[36] != ETX)                /* Packet is fixed length       */ {
            szTempBuff[0] = szRequestPkt[1];    /* Get the Return Code from Pkt */
            szTempBuff[1] = szRequestPkt[2];
            szTempBuff[2] = 0x00;

            if (inResult = atoi(szTempBuff))        /* Convert it and check...      */ {
                /**/pdebug(("inResult = %i", inResult)); /**/

                switch (inResult) {
                case PP_TIMEOUT:
                    /* An Error Occurred...      */
                    vdSetTranError(INV_PIN_ERR_MSG);
                    break;

                case PP_CANCEL:
                    /* An Error Occurred...      */
                    vdDisplayMessageFromFile(TXN_CANCELLED, WARNING_MSG);
                    /* The receipt was not printed at this point on PrePrinted */
                    vdSetTranError(TXN_CANCELLED);
                    if (fGetTRTPrintReceipt()) {
                        inGetReceiptTemplate(pobTran);
                        if (shGetTransactionCode(pobTran) == VOID)
                            pobTran->srBKey.fVoided = VS_TRUE;
                        if (shGetTransactionCode(pobTran) == ADJUST || shGetTransactionCode(pobTran) == TIP_ADJUST)
                            pobTran->srBRec.inNumberOfAdjusts = 1;
                        vdMakeReceiptTop(pobTran);
                        inPrintNCRReceiptTop(pobTran, &obPrinter);
                        inPrintError(pobTran, TXN_CANCELLED, VS_TRUE, &obPrinter);
                    }
                    break;

                case PP_CORRECT:
                    /* Corr pressed: code to loop*/
                    if (inGGCode() != DEBIT_BAL_INQ) /* Not on BalInq*/ {
                        vdBeep();
                        vdDisplayMessageFromFile(CUSTOMER_REQ_CORR_ERR_MSG, ERROR_MSG);
                        vdDisplayMessageFromFile(EMPTY_STR_ERR_MSG, STATUS_MSG);
                        inGetBaseAmount(pobTran);
                        inNCRGetTax(pobTran);
                        inGetCashBack(pobTran);
                        inGetTip(pobTran);
                        inComputeAndDispTotal(pobTran);
                        continue;
                    }
                    return(VS_ESCAPE);

                case PP_BADACCTLEN:
                    /* Acct Number to Short      */
                    vdSetTranError(INV_CARD_LEN_ERR_MSG21);
                    break;

                case PP_ERROR:
                    /* An Error Occurred...      */
                default:
                    /* Unknown Error Occurred... */
                    vdSetTranError(PINPAD_ERR_MSG21);
                        INCREMENT_PINPAD_ERROR;
                    break;
                }
                return(VS_ERR);
            }
        }
    } while (inResult == PP_CORRECT);
    /* Extract the information **********************************************/

    szTempBuff[0] = szRequestPkt[3] - 0x10; /* Get the account type         */
    szTempBuff[1] = 0x00;
    strcpy(pobTran->srBRec.unHostBatchRec.srNCR.szAcctType, szTempBuff);
    /**/pdebug(("Acct = %s", szTempBuff));/**/

    strncpy(szTempBuff, &szRequestPkt[4], 8);/* Get the MAC                 */
    szTempBuff[8] = 0x00;
    strcpy(pobTran->stMAC, szTempBuff);
    /**/pdebug(("MAC = %s", szTempBuff));/**/

    strncpy(szTempBuff, &szRequestPkt[12], 16);/* Get the PIN Block         */
    szTempBuff[16] = 0x00;
    strcpy(pobTran->szPIN, szTempBuff);
    /**/pdebug(("PIN = %s", szTempBuff));/**/

    return(VS_SUCCESS);
}


/*-------------------------------------------------------------------------
    Function : inPerformMacAnalysis
    Description : This functions processes an Interac response trans on the PP
    Parameters :
    Returns : PP_SUCCESS (0), PP_ERROR (1), PP_MACFAILED (7)
    Globals :
    Notes : none.
    Modification History :
      #     Date      Who                     Description
    ----- -------- ---------- ---------------------------------------------
      1   05/30/95 Carlos_R1  Created w/ minimum req's
      2   06/26/95 Carlos_R1  Completed the function
      3   11/28/95 Jose_F1    Do not use the HDT keys for S71 command.
                              The keys received in the SPDH response message
                              need to be sent to the PP.
      4   11/28/95 Jose_F1    Do not Use SPDH Transmision No. from HDT Stand. No.
      5   01/06/96 Jose_F1    Added error chequing on return inRecPINPadData
                              for message not received.
      6   11/13/98 Page_K1    Add encrypted amount to S72 MAC Data. 
      7   11/20/98 Page_K1    Pad term id to 16 in MAC Data
      8   12/17/98 Page_K1    Remove balance from data to be MACed
      9   02/19/99 Page_K1    Do not cut Auth Code to 6 digits in data to be MACed
     10   02/26/99 Page_K1    Remove unused local variables
--------------------------------------------------------------------------*/

int inPerformMacAnalysis(TRANSACTION_OBJECT* pobTran) {
    H00_REC srH00;
    TFM_REC srTFMRec;
    unsigned char szScratch[20];
    unsigned char szResponsePkt[ICPA_S72_SIZE + 1];
    unsigned char szTempBuff[45];
    int inResult;

    /** /pdebug(("-- inPerformMacAnalysis --"));*/

    memset(szResponsePkt, 0x00, ICPA_S72_SIZE + 1); /* Clear out the buffer */
    memset(szTempBuff, 0x00, 45);           /* Clear out the buffer         */

    /* Build the request packet *********************************************/

    /* simon_l1: Fix FID 'j' empty problem */
    if ((inGGCode() == DEBIT_BAL_INQ) && (fSPDHTransApproved(pobTran->srBRec.szRespCode) == VS_TRUE))
        /* end */ {
        szResponsePkt[0] = 'S';             /* Append Packet Command        */
        szResponsePkt[1] = '7';
        szResponsePkt[2] = '2';
        sprintf(&szResponsePkt[ICPA_CMD_LEN], "%c", (pobTran->srBRec.inLCTIndex + 0x30));
    } else {
        szResponsePkt[0] = 'S';             /* Append Packet Command        */
        szResponsePkt[1] = '7';
        szResponsePkt[2] = '1';
        sprintf(&szResponsePkt[ICPA_CMD_LEN], "%c", (pobTran->srBRec.inLCTIndex + 0x30));
    }

    strcpy(szTempBuff, pobTran->stMAC);
    /** /pdebug(("Host MAC = %s", szTempBuff));*/
    strcat(szResponsePkt, szTempBuff);      /* Append to request packet     */

    /* simon_l1: Fix FID 'j' empty problem */
    if ((inGGCode() == DEBIT_BAL_INQ) && (fSPDHTransApproved(pobTran->srBRec.szRespCode) == VS_TRUE)) {
        /** /pdebug(("Bal=%s",pobTran->szBalance));*/
        memset(szTempBuff, 0x00, 45);           /* Clear out the buffer         */
        szTempBuff[0] = FS;
        strncat(&szTempBuff[1], pobTran->szBalance, KEY_SIZE);  /* Append Encrypted Balance */
        strcat(szResponsePkt, szTempBuff);      /* Append to request packet     */
    }

    memset(szTempBuff, 0x00, 45);           /* Clear out the buffer         */
    szTempBuff[0] = FS;
    strcat(&szTempBuff[1], pobTran->stFldEncKey);  /* Append Fld Enc Key, if ex. */
    strcat(szResponsePkt, szTempBuff);      /* Append to request packet     */

    memset(szTempBuff, 0x00, 45);           /* Clear out the buffer         */
    szTempBuff[0] = FS;
    strcat(&szTempBuff[1], pobTran->szPINKey);  /* Append PIN Key, if exists    */
    strcat(szResponsePkt, szTempBuff);      /* Append to request packet     */

    memset(szTempBuff, 0x00, 45);           /* Clear out the buffer         */
    szTempBuff[0] = FS;
    strcat(&szTempBuff[1], pobTran->szKSN);  /* Append MAC Key, if exists    */
    strcat(szResponsePkt, szTempBuff);      /* Append to request packet     */

    memset(szTempBuff, 0x00, 45);           /* Clear out the buffer         */
    szTempBuff[0] = FS;
    strcat(&szTempBuff[1], pobTran->srBRec.unHostBatchRec.srNCR.szISORespCode);  /*ISORespCode*/
    strcat(szResponsePkt, szTempBuff);      /* Append to request packet     */

    memset(szTempBuff, 0x00, 45);           /* Clear out the buffer         */
    szTempBuff[0] = FS;
    strcat(&szTempBuff[1], pobTran->srBRec.szRespCode);     /* Get the response code        */
    strcat(szResponsePkt, szTempBuff);      /* Append to request packet     */

    /* Below is the data that will be MACed *********************************/

    memset(szTempBuff, 0x00, 45);           /* Clear out the buffer         */
    szTempBuff[0] = FS;

    strcpy(&szTempBuff[1], "00");          /* Do not use SPDH Trans. No.   */

    /** /pdebug(("TransNo = %s", szTempBuff));*/
    strcat(szResponsePkt, szTempBuff);      /* Append to request packet     */

    inLoadNCRParams(&srH00);
    strcpy(szTempBuff, srH00.szTermNum);
    pad(szTempBuff, szTempBuff, ' ', TERM_ID_SIZE, LEFT);
    /** /pdebug(("TermId = %s", szTempBuff));     Must be 16 bytes, as per SPDH*//**/
    strcat(szResponsePkt, szTempBuff);      /* Append to request packet     */

    /** /pdebug(("TranCode = %d", shGetTransactionCode(pobTran)));*/
    inGetTFMTransFields(shGetTransactionCode(pobTran), &srTFMRec, SPDH_SECOND_FID_SET);
    /** /pdebug(("TranCo = %d", srTFMRec.inSPDHTranCode));*/
    int2str(szTempBuff, srTFMRec.inSPDHTranCode);
    pad(szTempBuff, szTempBuff, '0', 2, RIGHT);
    strcat(szResponsePkt, szTempBuff);       /* Append to request packet     */

    strcpy(szTempBuff, pobTran->srBRec.szRespCode);     /* Get the response code        */
    /** /pdebug(("RespCo = %s", szTempBuff));*/
    strcat(szResponsePkt, szTempBuff);      /* Append to request packet     */

    strcpy(szTempBuff, pobTran->srBRec.szAuthCode);             /* Get the auth code            */
    /** /pdebug(("AuthCo = %s", szTempBuff));*/

    /* Do I need to do this? SPDH says that approval code should be 8 char */
    /* szTempBuff[6] = '\0'; */

    strcat(szResponsePkt, szTempBuff);      /* Append to request packet     */

    strcat(szResponsePkt, pobTran->szKSN);  /* Append MAC Key (H), if exists    */
    /** /pdebug(("MAC Key = %s", pobTran->szKSN));*/

    strcat(szResponsePkt, pobTran->stFldEncKey); /* Append Fld Enc Key (I), if ex. */
    /** /pdebug(("Fld Key = %s", pobTran->stFldEncKey));*/

    strcat(szResponsePkt, pobTran->szPINKey);  /* Append PIN Key (M), if exists    */
    /** /pdebug(("PIN Key = %s", pobTran->szPINKey));*/

    sprintf(szTempBuff, SUBSTITUTE_LD, pobTran->srBRec.lnSTAN);/* Get the sequence number      */
    pad(szTempBuff, szTempBuff, '0', 10, RIGHT);
    /** /pdebug(("SeqNum = %s", szTempBuff));*/
    strcat(szResponsePkt, szTempBuff);      /* Append to request packet     */

    /** /pdebug(("PktLen= %d", strlen(szResponsePkt)));*/

    /** /pdebug(("PP71=%s", szResponsePkt));*/
    /*  
        i=0;
        while (szResponsePkt[i] != NULL_CH)
        {
            pdebug(("PP71-%d-%02x",i,szResponsePkt[i]));
            i++;
        }
    */

    if (inSendPINPadData(szResponsePkt, inGetPPTimeOut(), VS_TRUE) == VS_ERR) {
        /** /pdebug(("Xmit Fail")); */
        return(PP_ERROR);
    }

    memset(szResponsePkt, 0x00, 10);        /* Clear out the buffer         */
    inResult = inRecPINPad501Data(szResponsePkt, inGetPPTimeOut(), sizeof(szResponsePkt));
    if (inResult == VS_ERR)
        return(PP_ERROR);

    /* Analyze the response *************************************************/

    szTempBuff[0] = szResponsePkt[1];   /* Get the Return Code from Pkt */
    szTempBuff[1] = szResponsePkt[2];
    szTempBuff[2] = 0x00;

    /** /pdebug(("PP Resp = %2.2s", szTempBuff));*/
    return(atoi(szTempBuff));           /* Convert it and return...         */
}


/*-------------------------------------------------------------------------
    Function : inGetICPASerNum
    Description : This functions gets the PINpad serial number and sets GDS
    Parameters :
    Returns : TRUE or FALSE
    Globals : none.
    Notes : none.
    Modification History :
      #     Date      Who                     Description
    ----- -------- ---------- ---------------------------------------------
      1   06/23/95 Carlos_R1  Created
      2   06/14/96 Jose_F1    Modified API "96" to "95".
      3   11/03/98 Page_K1    Use inRecPINPad501Data() instead of inRecPINPadData()
--------------------------------------------------------------------------*/

int inGetICPASerNum(TRANSACTION_OBJECT* pobTran) {
    unsigned char szTempBuff[ICPA_S95_RESP + 1];
    unsigned char chHowMany;

    /*  pdebug(("-- inGetICPASerNum --"));   */

    szTempBuff[0] = 'S';
    szTempBuff[1] = '9';
    szTempBuff[2] = '5';

    if (inSendPINPadData(szTempBuff, inGetPPTimeOut(), VS_TRUE) != VS_SUCCESS)
        return(VS_ERR);

    memset(szTempBuff, 0x00, ICPA_S95_RESP + 1);

    chHowMany = inRecPINPad501Data(szTempBuff, inGetPPTimeOut(), sizeof(szTempBuff));

    if (chHowMany != ICPA_S95_RESP)         /* Length is fixed for response */
        return(VS_ERR);

    memcpy(pobTran->stPINPadSerNum, &szTempBuff[ICPA_SERNUM_LOC], ICPA_SN_SIZE);
    pobTran->stPINPadSerNum[ICPA_SN_SIZE] = 0x00;

    return(VS_SUCCESS);
}

/*-------------------------------------------------------------------------
    Function : vdPPLoadWorkingKeys
    Description : This functions loads the recently received keys into the PP
    Parameters :
    Returns : TRUE or FALSE (error/no error)
    Globals : none.
    Notes : none.
    Modification History :
      #     Date      Who                     Description
    ----- -------- ---------- ---------------------------------------------
      1   07/24/95 Carlos_R1  Created
      2   11/03/98 Page_K1    Use inRecPINPad501Data() instead of inRecPINPadData()
--------------------------------------------------------------------------*/

int inPPLoadWorkingKeys(TRANSACTION_OBJECT* pobTran) {
    unsigned char szWorkingKeyBuff[ICPA_S84_SIZE + 1];
    unsigned char szTempBuff[17];

    /** /pdebug(("-- inPPLoadWorkingKeys --"));*/

    memset(szWorkingKeyBuff, 0x00, ICPA_S84_SIZE + 1);  /* Clear out the buffer */
    memset(szTempBuff, 0x00, 17);                   /* Clear out the buffer         */

    /* Build the request packet *********************************************/

    szWorkingKeyBuff[0] = 'S';                  /* Append Packet Command        */
    szWorkingKeyBuff[1] = '8';
    szWorkingKeyBuff[2] = '4';

    strcpy(&szWorkingKeyBuff[3], pobTran->stFldEncKey);
    szWorkingKeyBuff[19] = FS;

    strcpy(&szWorkingKeyBuff[20], pobTran->szPINKey);
    szWorkingKeyBuff[36] = FS;

    strcpy(&szWorkingKeyBuff[37], pobTran->szKSN);

    /** /pdebug(("PktLen= %d", strlen(szWorkingKeyBuff)));*/
    /** /pdebug(("WK Msg. = %s", szWorkingKeyBuff));*/

    if (inSendPINPadData(szWorkingKeyBuff, inGetPPTimeOut(), VS_TRUE) == VS_ERR) {
        /** /pdebug(("Xmit Fail"));*/
        return(VS_ERR);
    }

    memset(szWorkingKeyBuff, 0x00, ICPA_S84_SIZE);      /* Clear out the buffer */
    inRecPINPad501Data(szWorkingKeyBuff, inGetPPTimeOut(), sizeof(szWorkingKeyBuff));

    /* Analyze the response *************************************************/

    szTempBuff[0] = szWorkingKeyBuff[1];    /* Get the Return Code from Pkt */
    szTempBuff[1] = szWorkingKeyBuff[2];
    szTempBuff[2] = 0x00;

    /** /pdebug(("PP Resp = %2.2s", szTempBuff));*/
    /*  return(atoi(szTempBuff));            Convert it and return...       */
    return (inSetPINPadError(atoi(szTempBuff)));
}

/*-------------------------------------------------------------------------
    Function : fMACException
    Description :Returns VS_ERR if a transaction contains an ISO Response code
                which indicates a MAC exception. It does not need to be validated
                in the PINPad, else return VS_SUCCESS. It needs to be validated
                This is an Interac Regulation. 
    Parameters : 
    Returns : 
    Notes : none.
    Modification History :
      #     Date      Who                     Description
    ----- -------- ---------- ---------------------------------------------
      1   01/12/99 Page_K1    Remove obCommunication from param list for
                                inModifySPDHReversal()
--------------------------------------------------------------------------*/
VS_BOOL fMACException(TRANSACTION_OBJECT* pobTran,PACKET_OBJECT* pobPacket,char* pchISOCode) {
    int inCount,
    inIdx;
    char szRespCode[RESP_CODE_SIZE + 1];

    strcpy(szRespCode, pobTran->srBRec.szRespCode);

    inCount = strcmp(szRespCode, "999"); /* Front End Echoed Back, When is Down */

    if (inCount == 0) {
        /* szRespCode equal to a 999 */
        vdPP501AdvTransFailed(pobTran, szRespCode, pchISOCode);
        return(VS_ERR);  /* MAC Exception, Not send to validate */
    }
    switch (obCommunication.inLinkType) {
    case MODEM_COMM:
        /* 0=Dial                    */
    case COMM_3101:
        /* 3101 Public/Private             */
        inIdx = 1;                  /* Index for the SPDH, Skip STX */
        break;
    case COMM_3201:
        /* 3201 Text/Binary               */
        inIdx = 0;                  /* Index for the SPDH, no STX */
        break;
    default:
        return(VS_ERR);
    }

    switch (inPerformMacAnalysis(pobTran)) {
    case PP_SUCCESS:
        /* MAC Verified ...             */
        break;

    case PP_ERROR:
        /* Error with PINpad            */
    case PP_CANNOTREVERTKEYS:
    case PP_SC_FAILED:
        vdSetTranError(SYSTEM_ERR);
        inModifySPDHReversal(pobTran, &(pobPacket->pchReceiveBuff[inIdx]), MAC_REVERSAL); /* MAC, though PP Error */
        return(VS_ERR);

    case PP_MACFAILED:
        /* MAC Failed                   */
        vdSetTranError(MAC_DID_NOT_VERIFY);
        inModifySPDHReversal(pobTran, &(pobPacket->pchReceiveBuff[inIdx]), MAC_REVERSAL);
        return(VS_ERR);

    case PP_NOMACBLOCK:
        /* No MAC - Exception with PINpad */
        vdPP501AdvTransFailed(pobTran, szRespCode, pchISOCode);
        break;
    case PP_BADBALANCE:
        /* No C0 or D0                          */
    case PP_BADDECRYPTION:
        /* Decryption Error                     */
        vdSetTranError(DECRYPTION_ERROR);
        inModifySPDHReversal(pobTran, &(pobPacket->pchReceiveBuff[inIdx]), DECRYPTION_REVERSAL);
        return(VS_ERR);
    }

    /*  for (inCount = 0; inCount < MAX_MAC_ISO_CODES; inCount++)
        {
            sprintf(szISOCode, "%02d", inMACISOCodes[inCount]);
            if (strcmp(pchISOCode, szISOCode) == 0)
            {
                return(VS_FALSE); */ /* MAC Exception, Not send to validate */
    /*      }
        }  */
    return(VS_SUCCESS);  /* Sent to validate */
}


/*-----------------------------------------------------------------------------
* Routine name       :    inSetPINPadError
* Description        :    This routine handles the communication interface to 
the
*                         pinpad.
* Inputs             :    ->  The data that is to be sent to the pinpad.
*                         ->  The timeout within which the response is to be
*                             received.
* Output             :    ->  VS_SUCCESS if successful
*                             VS_ERR if error.
* Notes              :    ->  The application should set up a timeout by the
*                             1 second interrupt and.
*----------------------------------------------------------------------------*/
int inSetPINPadError(int inPPResp) {
    switch (inPPResp) {
    case PP_SUCCESS:
        /* MAC Verified ...             */
        break;

    case PP_ERROR:
        /* Error with PINpad            */
    case PP_CANNOTREVERTKEYS:
    case PP_SC_FAILED:
        vdSetTranError(SYSTEM_ERR);
        return(VS_ERR);

    case PP_MACFAILED:
        /* MAC Failed                   */
        vdSetTranError(MAC_DID_NOT_VERIFY);
        return(VS_ERR);

    case PP_NOMACBLOCK:
        /* No MAC - Exception with PINpad */
        break;

    case PP_BADBALANCE:
        /* No C0 or D0                          */
    case PP_BADDECRYPTION:
        /* Decryption Error                     */
        vdSetTranError(DECRYPTION_ERROR);
        return(VS_ERR);
    }
    return (VS_SUCCESS);
}



/*-----------------------------------------------------------------------------
* Routine Name     : inRecPINPad501Data
* Description      : Receives the data that is sent to the pinpad by the above
*                  : routine.
* Inputs           :
* Output           :
* Notes            : Receive buffer using this should be no smaller than
*                  : PINPAD_BUF_SIZE + 1
*
* Modification History:
*
*        #     Who      When               Why and What
*       --- --------- -------- --------------------------
*        1  Carlos_R1          Changed LRC check to work off number of bytes
*                              read and not using strlen....
*        3  Carlos_R1 06/22/95 Removed CANADA and made a inTimOut parameter
*                              mean you want NO timeout.
*        4  Carlos_R1 06/24/95 Added Parm3 - Max size of data to receive.
*----------------------------------------------------------------------------*/
int inRecPINPad501Data(char* pchRecBuf,int inTimOut,int inMaxRecSize) {
    int inLrc;
    char chLrchk;
    int insz = 100;
    char stRecBuf[PINPAD_BUF_SIZE + 1];
    int inNumRetry = 0;
    char chTempChar;

    memset(stRecBuf, 0x00, sizeof(stRecBuf));

    do {
        inNumRetry++;
        /** /pdebug(("Outer do=%d",inNumRetry));*/
        if (inTimOut)   /* If a timeout was passed */
            ulTimerCount = set_itimeout(NO_CLOCK_HANDLE inTimOut, TM_SECONDS);

        do                                      /* Check if Time Expired */ {
            /** /pdebug(("Inner do=%d",inNumRetry));*/

            if (inTimOut)   /* If a timeout was passed */ {
                if (CHECK_TIMEOUT(ulTimerCount) == 0)
                    break;  /* Timeout occurred */
            }

            /** /pdebug(("Do read=%d",insz));*/
        } while ((insz = read(inPINPad, stRecBuf, sizeof(stRecBuf) - 1)) == 0);
        /** /pdebug(("After inner do=%d",insz));*/
        if (insz == 0)
            break;                  /*  No response from pinpad - timed out */
        if (stRecBuf[0] == EOT)
            break;                  /* EOT received....break!               */
        inLrc = SVC_CRC_CALC(0, &(stRecBuf[1]), insz - 2);

        /* should check LRC and set OK */
        chLrchk = (stRecBuf[insz - 1] == inLrc);

        if (stRecBuf[insz - 1] == 0x00) /* If LRC == NULL, change so string */
            stRecBuf[insz - 1] = 0x01;    /* ops will function correctly later*/
        /* Call it paranoia                 */
        stRecBuf[insz] = 0x00;          /* Null the packet                  */

        /* send ACK or NAK depending on pinpad response */
        chTempChar = chLrchk ? ACK : NAK;

        write(inPINPad, &chTempChar, 1);
        /** /pdebug(("After ACK write=%02x",chTempChar));*/
    } while ((!chLrchk) && (inNumRetry < PP_MAX_RETRIES));
    /** /pdebug(("End=%d",insz));*/

    strncpy(pchRecBuf, stRecBuf, inMaxRecSize);         /* copy to appl buf  */
    /** /pdebug(("Resp=%s",pchRecBuf));*/
    return(((insz < inMaxRecSize) ? insz : inMaxRecSize));  /* Rtn Smaller of Two*/
}


#endif /* PP501_ICPA */

#undef VSPPICGE_C

